1
/******************************** Module Header *********************************\
2 * Module Name: MainForm.cs
3 * Project: CSWindowsHook
4 * Copyright (c) Microsoft Corporation.
6 * This example demonstrates how to set a hook that is specific to a thread as well
7 * as the global hook by using the low-level mouse and keyboard hooks in .NET. You
8 * can use hooks to monitor certain types of events. You can associate these events
9 * with a specific thread or with all the threads in the same desktop as the
12 * This source is subject to the Microsoft Public License.
13 * See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
14 * All other rights reserved.
17 * * 3/28/2009 11:04 PM Rongchun Zhang Created
18 * * 4/6/2009 1:26 PM Jialiang Ge Reviewed
19 \********************************************************************************/
21 #region Using directives
23 using System
.Collections
.Generic
;
24 using System
.ComponentModel
;
28 using System
.Windows
.Forms
;
29 using System
.Runtime
.InteropServices
;
30 using System
.Threading
;
31 using System
.Reflection
;
32 using System
.Diagnostics
;
36 namespace CSWindowsHook
38 public partial class MainForm
: Form
42 InitializeComponent();
45 private const int WM_KEYDOWN
= 0x100;
46 private const int WM_SYSKEYDOWN
= 0x104;
49 #region Local Mouse Hook
51 // Handle to the local mouse hook procedure
52 private IntPtr hLocalMouseHook
= IntPtr
.Zero
;
53 private HookProc localMouseHookCallback
= null;
56 /// Set local mouse hook
58 /// <returns></returns>
59 private bool SetLocalMouseHook()
61 // Create an instance of HookProc.
62 localMouseHookCallback
= new HookProc(this.MouseProc
);
64 hLocalMouseHook
= NativeMethods
.SetWindowsHookEx(
66 localMouseHookCallback
,
68 NativeMethod
.GetCurrentThreadId());
69 return hLocalMouseHook
!= IntPtr
.Zero
;
73 /// Remove the local mouse hook
75 /// <returns></returns>
76 private bool RemoveLocalMouseHook()
78 if (hLocalMouseHook
!= IntPtr
.Zero
)
80 // Unhook the mouse hook
81 if (!NativeMethods
.UnhookWindowsHookEx(hLocalMouseHook
))
84 hLocalMouseHook
= IntPtr
.Zero
;
90 /// Mouse hook procedure
91 /// The system calls this function whenever an application calls the
92 /// GetMessage or PeekMessage function and there is a mouse message to be
95 /// <param name="nCode">
96 /// The hook code passed to the current hook procedure.
97 /// When nCode equals HC_ACTION, the wParam and lParam parameters contain
98 /// information about a mouse message.
99 /// When nCode equals HC_NOREMOVE, the wParam and lParam parameters
100 /// contain information about a mouse message, and the mouse message has
101 /// not been removed from the message queue. (An application called the
102 /// PeekMessage function, specifying the PM_NOREMOVE flag.)
104 /// <param name="wParam">
105 /// Specifies the identifier of the mouse message.
107 /// <param name="lParam">Pointer to a MOUSEHOOKSTRUCT structure.</param>
108 /// <returns></returns>
109 /// <see cref="http://msdn.microsoft.com/en-us/library/ms644988.aspx"/>
110 private int MouseProc(int nCode
, IntPtr wParam
, IntPtr lParam
)
112 if (nCode
== HookCodes
.HC_ACTION
)
114 // Marshal the MOUSEHOOKSTRUCT data from the callback lParam
115 MOUSEHOOKSTRUCT mouseHookStruct
= (MOUSEHOOKSTRUCT
)
116 Marshal
.PtrToStructure(lParam
, typeof(MOUSEHOOKSTRUCT
));
118 // Get the mouse WM from the wParam parameter
119 MouseMessage wmMouse
= (MouseMessage
)wParam
;
121 // Display the current mouse coordinates and the message
122 String log
= String
.Format("X = {0} Y = {1} ({2})\r\n",
123 mouseHookStruct
.pt
.x
, mouseHookStruct
.pt
.y
, wmMouse
);
124 this.tbLog
.AppendText(log
);
127 // Pass the hook information to the next hook procedure in chain
128 return NativeMethods
.CallNextHookEx(hLocalMouseHook
, nCode
, wParam
, lParam
);
134 #region Global Low-level Mouse Hook
136 // Handle to the global low-level mouse hook procedure
137 private IntPtr hGlobalLLMouseHook
= IntPtr
.Zero
;
138 private HookProc globalLLMouseHookCallback
= null;
141 /// Set global low-level mouse hook
143 /// <returns></returns>
144 private bool SetGlobalLLMouseHook()
146 // Create an instance of HookProc.
147 globalLLMouseHookCallback
= new HookProc(this.LowLevelMouseProc
);
149 hGlobalLLMouseHook
= NativeMethods
.SetWindowsHookEx(
150 HookType
.WH_MOUSE_LL
, // Must be LL for the global hook
151 globalLLMouseHookCallback
,
152 // Get the handle of the current module
153 Marshal
.GetHINSTANCE(Assembly
.GetExecutingAssembly().GetModules()[0]),
154 // The hook procedure is associated with all existing threads running
155 // in the same desktop as the calling thread.
157 return hGlobalLLMouseHook
!= IntPtr
.Zero
;
161 /// Remove the global low-level mouse hook
163 /// <returns></returns>
164 private bool RemoveGlobalLLMouseHook()
166 if (hGlobalLLMouseHook
!= IntPtr
.Zero
)
168 // Unhook the low-level mouse hook
169 if (!NativeMethods
.UnhookWindowsHookEx(hGlobalLLMouseHook
))
172 hGlobalLLMouseHook
= IntPtr
.Zero
;
178 /// Low-level mouse hook procedure
179 /// The system call this function every time a new mouse input event is
180 /// about to be posted into a thread input queue. The mouse input can come
181 /// from the local mouse driver or from calls to the mouse_event function.
182 /// If the input comes from a call to mouse_event, the input was
183 /// "injected". However, the WH_MOUSE_LL hook is not injected into another
184 /// process. Instead, the context switches back to the process that
185 /// installed the hook and it is called in its original context. Then the
186 /// context switches back to the application that generated the event.
188 /// <param name="nCode">
189 /// The hook code passed to the current hook procedure.
190 /// When nCode equals HC_ACTION, the wParam and lParam parameters contain
191 /// information about a mouse message.
193 /// <param name="wParam">
194 /// This parameter can be one of the following messages:
195 /// WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL,
196 /// WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP.
198 /// <param name="lParam">Pointer to an MSLLHOOKSTRUCT structure.</param>
199 /// <returns></returns>
200 /// <see cref="http://msdn.microsoft.com/en-us/library/ms644986.aspx"/>
201 public int LowLevelMouseProc(int nCode
, IntPtr wParam
, IntPtr lParam
)
205 // Marshal the MSLLHOOKSTRUCT data from the callback lParam
206 MSLLHOOKSTRUCT mouseLLHookStruct
= (MSLLHOOKSTRUCT
)
207 Marshal
.PtrToStructure(lParam
, typeof(MSLLHOOKSTRUCT
));
209 // Get the mouse WM from the wParam parameter
210 MouseMessage wmMouse
= (MouseMessage
)wParam
;
212 // Display the current mouse coordinates and the message
213 String log
= String
.Format("X = {0} Y = {1} ({2})\r\n",
214 mouseLLHookStruct
.pt
.x
, mouseLLHookStruct
.pt
.y
, wmMouse
);
215 this.tbLog
.AppendText(log
);
218 // Pass the hook information to the next hook procedure in chain
219 return NativeMethods
.CallNextHookEx(hGlobalLLMouseHook
, nCode
, wParam
, lParam
);
225 #region Local Keyboard Hook
227 // Handle to the local keyboard hook procedure
228 private IntPtr hLocalKeyboardHook
= IntPtr
.Zero
;
229 private HookProc localKeyboardHookCallback
= null;
232 /// Set local keyboard hook
234 /// <returns></returns>
235 private bool SetLocalKeyboardHook()
237 // Create an instance of HookProc.
238 localKeyboardHookCallback
= new HookProc(this.KeyboardProc
);
240 hLocalKeyboardHook
= NativeMethods
.SetWindowsHookEx(
241 HookType
.WH_KEYBOARD
,
242 localKeyboardHookCallback
,
244 NativeMethod
.GetCurrentThreadId());
245 return hLocalKeyboardHook
!= IntPtr
.Zero
;
249 /// Remove the local keyboard hook
251 /// <returns></returns>
252 private bool RemoveLocalKeyboardHook()
254 if (hLocalKeyboardHook
!= IntPtr
.Zero
)
256 // Unhook the mouse hook
257 if (!NativeMethods
.UnhookWindowsHookEx(hLocalKeyboardHook
))
260 hLocalKeyboardHook
= IntPtr
.Zero
;
266 /// Keyboard hook procedure
267 /// The system calls this function whenever an application calls the
268 /// GetMessage or PeekMessage function and there is a keyboard message
269 /// (WM_KEYUP or WM_KEYDOWN) to be processed.
271 /// <param name="nCode">
272 /// The hook code passed to the current hook procedure.
273 /// When nCode equals HC_ACTION, the wParam and lParam parameters contain
274 /// information about a keystroke message.
275 /// When nCode equals HC_NOREMOVE, the wParam and lParam parameters
276 /// contain information about a keystroke message, and the keystroke
277 /// message has not been removed from the message queue. (An application
278 /// called the PeekMessage function, specifying the PM_NOREMOVE flag.)
280 /// <param name="wParam">
281 /// Specifies the virtual-key code of the key that generated the keystroke
282 /// message. http://msdn.microsoft.com/en-us/library/dd375731.aspx
284 /// <param name="lParam">
285 /// Specifies the repeat count, scan code, extended-key flag, context code,
286 /// previous key-state flag, and transition-state flag.
287 /// http://msdn.microsoft.com/en-us/library/ms646267.aspx#_win32_Keystroke_Message_Flags
289 /// <returns></returns>
290 /// <see cref="http://msdn.microsoft.com/en-us/library/ms644984.aspx"/>
291 public int KeyboardProc(int nCode
, IntPtr wParam
, IntPtr lParam
)
293 if (nCode
== HookCodes
.HC_ACTION
)
295 // Get the virtual key code from wParam
296 // http://msdn.microsoft.com/en-us/library/dd375731.aspx
297 Keys vkCode
= (Keys
)wParam
;
299 // Get the keystroke message flags
300 // http://msdn.microsoft.com/en-us/library/ms646267.aspx#_win32_Keystroke_Message_Flags
301 string flag
= lParam
.ToString("X8");
303 string log
= String
.Format("Virtual-Key code: {0} Flag: {1}\r\n",
305 this.tbLog
.AppendText(log
);
308 // Pass the hook information to the next hook procedure in chain
309 return NativeMethods
.CallNextHookEx(hLocalKeyboardHook
, nCode
, wParam
, lParam
);
315 #region Global Low-level Keyboard Hook
317 // Handle to the global low-level keyboard hook procedure
318 private IntPtr hGlobalLLKeyboardHook
= IntPtr
.Zero
;
319 private HookProc globalLLKeyboardHookCallback
= null;
322 /// Set global low-level keyboard hook
324 /// <returns></returns>
325 private bool SetGlobalLLKeyboardHook()
327 // Create an instance of HookProc.
328 globalLLKeyboardHookCallback
= new HookProc(this.LowLevelKeyboardProc
);
330 hGlobalLLKeyboardHook
= NativeMethods
.SetWindowsHookEx(
331 HookType
.WH_KEYBOARD_LL
, // Must be LL for the global hook
332 globalLLKeyboardHookCallback
,
333 // Get the handle of the current module
334 Marshal
.GetHINSTANCE(Assembly
.GetExecutingAssembly().GetModules()[0]),
335 // The hook procedure is associated with all existing threads running
336 // in the same desktop as the calling thread.
338 return hGlobalLLKeyboardHook
!= IntPtr
.Zero
;
342 /// Remove the global low-level keyboard hook
344 /// <returns></returns>
345 private bool RemoveGlobalLLKeyboardHook()
347 if (hGlobalLLKeyboardHook
!= IntPtr
.Zero
)
349 // Unhook the mouse hook
350 if (!NativeMethods
.UnhookWindowsHookEx(hGlobalLLKeyboardHook
))
353 hGlobalLLKeyboardHook
= IntPtr
.Zero
;
359 /// Low-level keyboard hook procedure.
360 /// The system calls this function every time a new keyboard input event
361 /// is about to be posted into a thread input queue. The keyboard input
362 /// can come from the local keyboard driver or from calls to the
363 /// keybd_event function. If the input comes from a call to keybd_event,
364 /// the input was "injected". However, the WH_KEYBOARD_LL hook is not
365 /// injected into another process. Instead, the context switches back
366 /// to the process that installed the hook and it is called in its
367 /// original context. Then the context switches back to the application
368 /// that generated the event.
370 /// <param name="nCode">
371 /// The hook code passed to the current hook procedure.
372 /// When nCode equals HC_ACTION, it means that the wParam and lParam
373 /// parameters contain information about a keyboard message.
375 /// <param name="wParam">
376 /// The parameter can be one of the following messages:
377 /// WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP.
379 /// <param name="lParam">Pointer to a KBDLLHOOKSTRUCT structure.</param>
380 /// <returns></returns>
381 /// <see cref="http://msdn.microsoft.com/en-us/library/ms644985.aspx"/>
382 public int LowLevelKeyboardProc(int nCode
, IntPtr wParam
, IntPtr lParam
)
386 // Marshal the KeyboardHookStruct data from the callback lParam
387 KBDLLHOOKSTRUCT keyboardLLHookStruct
= (KBDLLHOOKSTRUCT
)
388 Marshal
.PtrToStructure(lParam
, typeof(KBDLLHOOKSTRUCT
));
390 // Get the virtual key code from KBDLLHOOKSTRUCT.vkCode
391 // http://msdn.microsoft.com/en-us/library/dd375731.aspx
392 Keys vkCode
= (Keys
)keyboardLLHookStruct
.vkCode
;
394 // Get the keyboard WM from the wParam parameter
395 KeyboardMessage wmKeyboard
= (KeyboardMessage
)wParam
;
397 // Display the current mouse coordinates and the message
398 String log
= String
.Format("Virtual-Key code: {0} ({1})\r\n",
400 this.tbLog
.AppendText(log
);
403 // Pass the hook information to the next hook procedure in chain
404 return NativeMethods
.CallNextHookEx(hGlobalLLKeyboardHook
, nCode
, wParam
, lParam
);
410 #region Form Event Handlers
412 private void btnLocalMouseHook_Click(object sender
, EventArgs e
)
414 if (hLocalMouseHook
== IntPtr
.Zero
)
416 // Set the local mouse hook
417 if (SetLocalMouseHook())
419 btnLocalMouseHook
.Text
= "Unhook Local Mouse Hook";
420 btnGlobalLLMouseHook
.Enabled
= false;
424 MessageBox
.Show("SetWindowsHookEx(Mouse) failed");
429 // Remove the local mouse hook
430 if (RemoveLocalMouseHook())
432 btnLocalMouseHook
.Text
= "Set Local Mouse Hook";
433 btnGlobalLLMouseHook
.Enabled
= true;
437 MessageBox
.Show("UnhookWindowsHookEx(Mouse) failed");
442 private void btnGlobalLLMouseHook_Click(object sender
, EventArgs e
)
444 if (hGlobalLLMouseHook
== IntPtr
.Zero
)
446 // Set the global low-level mouse hook
447 if (SetGlobalLLMouseHook())
449 btnGlobalLLMouseHook
.Text
= "Unhook Global LL Mouse Hook";
450 btnLocalMouseHook
.Enabled
= false;
454 MessageBox
.Show("SetWindowsHookEx(LL Mouse) failed");
459 // Remove the global low-level mouse hook
460 if (RemoveGlobalLLMouseHook())
462 btnGlobalLLMouseHook
.Text
= "Set Global LL Mouse Hook";
463 btnLocalMouseHook
.Enabled
= true;
467 MessageBox
.Show("UnhookWindowsHookEx(LL Mouse) failed");
472 private void btnLocalKeyboardHook_Click(object sender
, EventArgs e
)
474 if (hLocalKeyboardHook
== IntPtr
.Zero
)
476 // Set the local keyboard hook
477 if (SetLocalKeyboardHook())
479 btnLocalKeyboardHook
.Text
= "Unhook Local Keyboard Hook";
480 btnGlobalLLKeyboardHook
.Enabled
= false;
484 MessageBox
.Show("SetWindowsHookEx(Keyboard) failed");
489 // Remove the local keyboard hook
490 if (RemoveLocalKeyboardHook())
492 btnLocalKeyboardHook
.Text
= "Set Local Keyboard Hook";
493 btnGlobalLLKeyboardHook
.Enabled
= true;
497 MessageBox
.Show("UnhookWindowsHookEx(Keyboard) failed");
502 private void btnGlobalLLKeyboardHook_Click(object sender
, EventArgs e
)
504 if (hGlobalLLKeyboardHook
== IntPtr
.Zero
)
506 // Set the global low-level keyboard hook
507 if (SetGlobalLLKeyboardHook())
509 btnGlobalLLKeyboardHook
.Text
= "Unhook Global LL Keyboard Hook";
510 btnLocalKeyboardHook
.Enabled
= false;
514 MessageBox
.Show("SetWindowsHookEx(LL KeyBoard) failed");
519 // Remove the global low-level keyboard hook
520 if (RemoveGlobalLLKeyboardHook())
522 btnGlobalLLKeyboardHook
.Text
= "Set Global LL Keyboard Hook";
523 btnLocalKeyboardHook
.Enabled
= true;
527 MessageBox
.Show("UnhookWindowsHookEx(LL Keyboard) failed");
533 /// Remove all hooks if set when closing form
535 /// <param name="sender"></param>
536 /// <param name="e"></param>
537 private void FrmMain_FormClosing(object sender
, FormClosingEventArgs e
)
539 RemoveLocalMouseHook();
540 RemoveGlobalLLMouseHook();
541 RemoveLocalKeyboardHook();
542 RemoveGlobalLLKeyboardHook();